// Copyright 2017 Baidu Inc. All Rights Reserved.
// Author: Zhang Leliang (zhangleliang@baidu.com)
//
// Description: baidu common used timer

#include "baidu_common_timer.h"
#include "baidu_ca_scheduler.h"
#include "baidu_media_manager.h"
#include "baidu_json.h"
#include "baidu_ca_object.h"
#include "duer_log.h"

namespace duer {

BaiduCommonTimer::ChildrenPuzzleSilentTimerArgument::ChildrenPuzzleSilentTimerArgument(
        const char* argument) : _resource(argument) {
}

BaiduCommonTimer::ChildrenPuzzleSilentTimerArgument::~ChildrenPuzzleSilentTimerArgument() {
    if (_resource != NULL) {
        baidu_json_release(const_cast<char*>(_resource));
        _resource = NULL;
    }
}

const char* BaiduCommonTimer::ChildrenPuzzleSilentTimerArgument::get_resource() const {
    return _resource;
}

BaiduCommonTimer::WordsChainSilentTimerArgument::WordsChainSilentTimerArgument(
        const char* argument) : _resource(argument) {
}

BaiduCommonTimer::WordsChainSilentTimerArgument::~WordsChainSilentTimerArgument() {
    if (_resource != NULL) {
        baidu_json_release(const_cast<char*>(_resource));
        _resource = NULL;
    }
}

const char* BaiduCommonTimer::WordsChainSilentTimerArgument::get_resource() const {
    return _resource;
}

int BaiduCommonTimer::AudioPlayListener::on_start() {
    // do nothing
    return 0;
}

int BaiduCommonTimer::AudioPlayListener::on_stop() {
    audio_play_stop_call_back();
    return 0;
}

int BaiduCommonTimer::AudioPlayListener::on_finish() {
    audio_play_stop_call_back();
    return 0;
}

volatile baidu_timer_t BaiduCommonTimer::_s_children_puzzle_silent_timer;
volatile iot_mutex_t BaiduCommonTimer::_s_children_puzzle_silent_timer_mutex = iot_mutex_create();

int BaiduCommonTimer::create_s_children_puzzle_silent_timer(
        int interval, void* priv_data) {
    DUER_LOGV("interval:%d, priv_data: %p", interval, priv_data);
    int ret = TIMER_RET_OK;
    iot_mutex_lock(_s_children_puzzle_silent_timer_mutex, 0);
    if (_s_children_puzzle_silent_timer) {
        baidu_timer_destroy(_s_children_puzzle_silent_timer);
        _s_children_puzzle_silent_timer = NULL;
    }

    _s_children_puzzle_silent_timer =
        baidu_timer_create(children_puzzle_silent_timer_callback,
                           release_children_puzzle_silent_timer_priv_data,
                           TIMER_TYPE_ONCE, priv_data, interval);

    if (_s_children_puzzle_silent_timer == NULL) {
        DUER_LOGE("create children_puzzle_silent_timer fail!!");
        ret = TIMER_RET_INTERNAL_ERR;
    }
    iot_mutex_unlock(_s_children_puzzle_silent_timer_mutex);
    return ret;
}

int BaiduCommonTimer::start_s_children_puzzle_silent_timer() {
    int ret = TIMER_RET_INTERNAL_ERR;
    iot_mutex_lock(_s_children_puzzle_silent_timer_mutex, 0);
    if (_s_children_puzzle_silent_timer) {
        ret = baidu_timer_start(_s_children_puzzle_silent_timer);
    } else {
        DUER_LOGW("timer is already destroy!");
    }
    iot_mutex_unlock(_s_children_puzzle_silent_timer_mutex);
    if (ret == TIMER_RET_OK) {
        DUER_LOGI("_s_children_puzzle_silent_timer started!!");
    }
    return ret;
}

int BaiduCommonTimer::stop_s_children_puzzle_silent_timer() {
    int ret = TIMER_RET_OK;

    //because this stop function will be invoked frequently
    //and most of the time _s_children_puzzle_silent_timer is null(not in puzzle)
    //so here the outer 'if' will improve the performance without to acquire the mutex
    if (_s_children_puzzle_silent_timer) {
        iot_mutex_lock(_s_children_puzzle_silent_timer_mutex, 0);
        if (_s_children_puzzle_silent_timer) {
            ret = baidu_timer_stop(_s_children_puzzle_silent_timer);
            ret = baidu_timer_destroy(_s_children_puzzle_silent_timer);
            _s_children_puzzle_silent_timer = NULL;
        } else {
            DUER_LOGW("timer is already destroy!!!");
        }
        iot_mutex_unlock(_s_children_puzzle_silent_timer_mutex);
        DUER_LOGI("_s_children_puzzle_silent_timer stopped!!");
    }
    return ret;
}

void BaiduCommonTimer::release_children_puzzle_silent_timer_priv_data(void* argument) {
    if (argument == NULL) {
        DUER_LOGW("argument is NULL");
        return;
    }
    ChildrenPuzzleSilentTimerArgument* cpst
        = static_cast<ChildrenPuzzleSilentTimerArgument*>(argument);
    delete cpst;
}

void BaiduCommonTimer::children_puzzle_silent_timer_callback(void* argument) {
    DUER_LOGV("enter timer callback argument: %p", argument);

    ChildrenPuzzleSilentTimerArgument* cpst
        = static_cast<ChildrenPuzzleSilentTimerArgument*>(argument);
    if (cpst == NULL) {
        DUER_LOGW("argument is NULL");
        return;
    }

    DUER_LOGV("resource: %s", cpst->get_resource());
    baidu_json* resource = baidu_json_Parse(cpst->get_resource());
    baidu_json_AddStringToObject(resource, "puzzle_directive", "1");

    //puzzle_control and resource will be released when data destroy
    Object puzzle_control;
    puzzle_control.receiveObject("puzzle_control", resource);
    Scheduler::instance().report(puzzle_control);
    DUER_LOGV("puzzle_control: %s", puzzle_control.toString());
}

volatile baidu_timer_t BaiduCommonTimer::_s_wordschain_silent_timer;
volatile iot_mutex_t BaiduCommonTimer::_s_wordschain_silent_timer_mutex = iot_mutex_create();
BaiduCommonTimer::AudioPlayListener BaiduCommonTimer::_s_audio_play_listener;

int BaiduCommonTimer::create_s_wordschain_silent_timer(
        int interval, void* priv_data) {
    DUER_LOGV("interval:%d, priv_data: %p", interval, priv_data);
    int ret = TIMER_RET_OK;
    iot_mutex_lock(_s_wordschain_silent_timer_mutex, 0);
    if (_s_wordschain_silent_timer) {
        baidu_timer_destroy(_s_wordschain_silent_timer);
        _s_wordschain_silent_timer = NULL;
    }

    _s_wordschain_silent_timer =
        baidu_timer_create(wordschain_silent_timer_callback,
                           release_wordschain_silent_timer_priv_data,
                           TIMER_TYPE_ONCE, priv_data, interval);

    if (_s_wordschain_silent_timer == NULL) {
        DUER_LOGE("create _s_wordschain_silent_timer fail!!");
        ret = TIMER_RET_INTERNAL_ERR;
    }
    iot_mutex_unlock(_s_wordschain_silent_timer_mutex);
    return ret;
}

int BaiduCommonTimer::start_s_wordschain_silent_timer() {
    int ret = TIMER_RET_INTERNAL_ERR;
    iot_mutex_lock(_s_wordschain_silent_timer_mutex, 0);
    if (_s_wordschain_silent_timer) {
        ret = baidu_timer_start(_s_wordschain_silent_timer);
    } else {
        DUER_LOGW("wordschain timer is already destroy!");
    }
    iot_mutex_unlock(_s_wordschain_silent_timer_mutex);
    if (ret == TIMER_RET_OK) {
        DUER_LOGI("_s_wordschain_silent_timer started!!");
    }
    return ret;
}

int BaiduCommonTimer::stop_s_wordschain_silent_timer() {
    int ret = TIMER_RET_OK;

    //because this stop function will be invoked frequently
    //and most of the time _s_wordschain_silent_timer is null
    //so here the outer 'if' will improve the performance without to acquire the mutex
    if (_s_wordschain_silent_timer) {
        iot_mutex_lock(_s_wordschain_silent_timer_mutex, 0);
        if (_s_wordschain_silent_timer) {
            ret = baidu_timer_stop(_s_wordschain_silent_timer);
            ret = baidu_timer_destroy(_s_wordschain_silent_timer);
            _s_wordschain_silent_timer = NULL;
        } else {
            DUER_LOGW("wordschain timer is already destroy!!!");
        }
        iot_mutex_unlock(_s_wordschain_silent_timer_mutex);
        DUER_LOGI("_s_wordschain_silent_timer stopped!!");
    }
    return ret;
}

void BaiduCommonTimer::release_wordschain_silent_timer_priv_data(void* argument) {
    if (argument == NULL) {
        DUER_LOGW("wordschain argument is NULL");
        return;
    }
    WordsChainSilentTimerArgument* wcst
        = static_cast<WordsChainSilentTimerArgument*>(argument);
    delete wcst;
}

void BaiduCommonTimer::wordschain_silent_timer_callback(void* argument) {
    DUER_LOGV("enter wordschain timer callback argument: %p", argument);

    WordsChainSilentTimerArgument* wcst
        = static_cast<WordsChainSilentTimerArgument*>(argument);
    if (wcst == NULL) {
        DUER_LOGW("wordschain argument is NULL");
        return;
    }

    DUER_LOGV("resource: %s", wcst->get_resource());
    baidu_json* resource = baidu_json_Parse(wcst->get_resource());
    baidu_json_AddStringToObject(resource, "wordschain_directive", "1");

    //puzzle_control and resource will be released when data destroy
    Object wordschain_control;
    wordschain_control.receiveObject("wordschain_control", resource);
    Scheduler::instance().report(wordschain_control);
    DUER_LOGV("wordschain_control: %s", wordschain_control.toString());
}

void BaiduCommonTimer::audio_play_stop_call_back() {
    if (_s_children_puzzle_silent_timer) {
        start_s_children_puzzle_silent_timer();
    }
    if (_s_wordschain_silent_timer) {
        start_s_wordschain_silent_timer();
    }
    MediaManager::instance().unregister_listener(get_audio_play_listener());
}

void BaiduCommonTimer::stop_silent_timer() {
    stop_s_children_puzzle_silent_timer();
    stop_s_wordschain_silent_timer();
}

MediaPlayerListener* BaiduCommonTimer::get_audio_play_listener() {
    return &_s_audio_play_listener;
}

} // namespace duer
